package com.ponhu.ea2.goodsCenter.provider.facade.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.nacos.common.utils.BiFunction;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ponhu.ea2.common.basic.model.PagedResult;
import com.ponhu.ea2.common.basic.model.ResponseBean;
import com.ponhu.ea2.constants.CommonConstant;
import com.ponhu.ea2.dto.LogsForTextAddDto;
import com.ponhu.ea2.dto.PhMaterialDto;
import com.ponhu.ea2.dto.PhMaterialEditDto;
import com.ponhu.ea2.entity.*;
import com.ponhu.ea2.enums.ApplyCrowdEnum;
import com.ponhu.ea2.enums.MaterialSourceEnum;
import com.ponhu.ea2.enums.ResponseCodeEnum;
import com.ponhu.ea2.enums.YesOrNoEnum;
import com.ponhu.ea2.goodsCenter.provider.biz.OperationRecordLogsCoreService;
import com.ponhu.ea2.goodsCenter.provider.mapper.MaterialMapper;
import com.ponhu.ea2.goodsCenter.provider.mapper.PhCategoryMapper;
import com.ponhu.ea2.goodsCenter.provider.mapper.PhMaterialPropertyValueMdMapper;
import com.ponhu.ea2.goodsCenter.provider.mapper.PhModelMapper;
import com.ponhu.ea2.goodsCenter.provider.service.*;
import com.ponhu.ea2.goodsCenter.provider.utils.BaoxjUtils;
import com.ponhu.ea2.goodsCenter.provider.utils.CommonUtils;
import com.ponhu.ea2.goodsCenter.provider.utils.LocalDateUtil;
import com.ponhu.ea2.goodsCenter.provider.utils.ProBeanUtils;
import com.ponhu.ea2.query.MaterialInfoQuery;
import com.ponhu.ea2.service.facade.InventoryApiService;
import com.ponhu.ea2.service.facade.PhMaterialInfoApiService;
import com.ponhu.ea2.support.exception.BusinessRuntimeException;
import com.ponhu.ea2.utils.QiNiuUploadUtils;
import com.ponhu.ea2.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

import static com.ponhu.ea2.enums.ResponseCodeEnum.INVENTORY_EDIT_EXCEPTION_800216;
import static com.ponhu.ea2.enums.ResponseCodeEnum.PARAMETER_IS_INCORRECT_800001;

/**
 * @author luyu
 * @create 2021-10-18 17:49
 */
@Slf4j
@DubboService(version = "1.0.0", interfaceClass = PhMaterialInfoApiService.class, timeout = 6000,
        methods = {@Method(name = "aiPictureClassifyBatchSync", timeout = 1800000)})
public class PhMaterialInfoApiServiceImpl implements PhMaterialInfoApiService {

    @Resource
    BaoxjUtils baoxjUtils;

    @Resource
    MaterialMapper materialMapper;

    @Resource
    PhCategoryMapper phCategoryMapper;

    @Resource
    PhMaterialPropertyValueMdMapper phMaterialPropertyValueMdMapper;

    @Resource
    PhModelService phModelService;

    @Resource
    PhSeriesService phSeriesService;

    @Resource
    PhMaterialPropertyValueMdService phMaterialPropertyValueMdService;

    @Resource
    OperationRecordLogsCoreService operationRecordLogsCoreService;

    @Resource
    PhModelMapper phModelMapper;

    @Resource
    MaterialService materialService;

    @Resource
    InventoryService inventoryService;

    @Resource
    InventoryApiService inventoryApiService;

    @Resource
    PhAiPictureMaterialMdService phAiPictureMaterialMdService;

    @Override
    public PagedResult<PhMaterialVo> queryPhMaterialInfo(MaterialInfoQuery materialInfoQuery) {

        Page page = new Page(materialInfoQuery.getPageIndex(), materialInfoQuery.getPageSize());
        IPage<PhMaterialVo> phMaterialVos = (IPage) materialMapper.queryPhMaterialInfo(page, materialInfoQuery);

        PagedResult<PhMaterialVo> pagedResult = new PagedResult(phMaterialVos.getRecords(),
                (int) phMaterialVos.getCurrent(),
                phMaterialVos.getTotal(),
                (int) phMaterialVos.getSize());
        pagedResult.setPageSize((int) phMaterialVos.getSize());
        return pagedResult;
    }

    @Override
    public ResponseBean<PhMaterialVo> getMaterialInfoByCode(String materialCode) {
        PhMaterialVo phMaterialVo = materialMapper.getMaterialVoByCode(materialCode);
        if (ObjectUtils.isNotEmpty(phMaterialVo)) {
            // 物料属性针对多选属性,转化属性名称和属性值
            List<PhPropertyVo> propertyVoList = phMaterialPropertyValueMdService
                    .getPropertyValueMap(phMaterialVo.getMaterialId());

            phMaterialVo.setMaterialPropertyValueList(propertyVoList);
        }
        return ResponseBean.ok(phMaterialVo);
    }

    @Override
    public ResponseBean<PhMaterialVo> getMaterialInfoByModelId(Integer modelId) {
        PhMaterialVo phMaterialVo = materialMapper.getMaterialVoByModelId(modelId);
        if (ObjectUtils.isNotEmpty(phMaterialVo)) {
            // 物料属性
            List<PhMaterialPropertyValueMd> phMaterialPropertyValueMds = phMaterialPropertyValueMdMapper
                    .selectList(new LambdaQueryWrapper<PhMaterialPropertyValueMd>()
                            .eq(PhMaterialPropertyValueMd::getMaterialId, phMaterialVo.getMaterialId()));
            // 针对多选属性,转化属性名称和属性值
            List<PhPropertyVo> propertyVoList = CommonUtils.packagingPhPropertyVo(
                    phMaterialPropertyValueMds);
            phMaterialVo.setMaterialPropertyValueList(propertyVoList);
        }
        return ResponseBean.ok(phMaterialVo);
    }

    /*public Material checkPhMaterial(Long brandId, String model) {
        if (StringUtils.isNotBlank(model)) {
            // 无型号不匹配、去除特殊符号下划线连接符、去除前后空格和中间空格、去除大小写）
            PhModel phModel = phModelMapper.getModelByName(filterModel(model), brandId);
            if (ObjectUtils.isEmpty(phModel)) {
                return null;
            }
            // 根据型号id和品牌id查询物料,判断物料是否已经存在,如果存在则直接返回已有物料
            List<Material> materials = materialMapper.selectList(
                    new LambdaQueryWrapper<Material>()
                            .eq(Material::getBrandId, brandId)
                            .eq(Material::getModelId, phModel.getId()));
            if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(materials)) {
                return materials.get(0);
            }
        }
        return null;
    }*/

    public String filterModel(String model) {
        return model.toLowerCase()
                .replaceAll(" ", "")
                .replaceAll("_", "")
                .replaceAll("-", "");
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean savePhMaterial(PhMaterialDto phMaterialDto) {
        // 校验系列,如果没有则创建
        PhSeries phSeries = phSeriesService.checkSeriesByName(
                phMaterialDto.getSeries(),
                phMaterialDto.getBrandId());
        // 校验型号,如果没有,则创建
        PhModel phModel = phModelService.checkPhModelByName(phMaterialDto.getModel(), phSeries.getId(), phMaterialDto.getBrandId());
        Material material = assembleData(phMaterialDto, phSeries, phModel);
        if (!materialService.save(material)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .MATERIAL_CREATE_EXCEPTION_800402);
        }

        // 包小检AI识图关联物料
        if (StringUtils.isNotEmpty(phMaterialDto.getItemId()))
            phAiPictureMaterialMdService.updateAiPictureByItemId(
                    phMaterialDto.getItemId(), material.getId(), material.getMaterialCode());

        // 记录日志
        CompletableFuture.runAsync(() -> {
            LogsForTextAddDto logsForTextAddDto = new LogsForTextAddDto()
                    .setBusinessTable(CommonConstant.BUSINESS_TYPE_MASTERIAL)
                    .setRecordId(material.getId())
                    .setOperatorId(phMaterialDto.getOperatorId())
                    .setOperator(phMaterialDto.getOperatorName())
                    .setTextContent("物料创建成功");
            operationRecordLogsCoreService.addTextAddOperationRecordLogs(logsForTextAddDto);
        });
        // 写入物料属性中间表
        if (CollectionUtils.isNotEmpty(phMaterialDto.getPropertyList())) {
            phMaterialPropertyValueMdService.savePhmaterialProperty(
                    phMaterialDto.getPropertyList(),
                    material);
        }
        return true;
    }

    @Override
    public ResponseBean<PhMaterialVo> getPhMaterialById(String materialId) {
        PhMaterialVo phMaterialVo = materialMapper.getMaterialVoById(materialId);
        if (ObjectUtils.isNotEmpty(phMaterialVo)) {
            List<PhPropertyEditVo> propertyValueVos = phMaterialPropertyValueMdService
                    .getPropertyValueVos(materialId, phMaterialVo.getThirdCategoryId());
            phMaterialVo.setPhPropertyVos(propertyValueVos);
        }
        return ResponseBean.ok(phMaterialVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editPhMaterial(PhMaterialEditDto phMaterialEditDto) {

        //Material material = materialMapper.selectById(phMaterialEditDto.getId());
        PhMaterialVo material = materialMapper.getMaterialVoById(phMaterialEditDto.getId().toString());
        return editPhMaterialWithRecord(phMaterialEditDto, material, (ed, inv) -> {

            // 编辑物料基本信息
            editPhMaterialData(phMaterialEditDto, material);

            // 包小检AI识图关联物料
            if (StringUtils.isNotEmpty(phMaterialEditDto.getItemId()))
                phAiPictureMaterialMdService.updateAiPictureByItemId(
                        phMaterialEditDto.getItemId(), material.getMaterialId(), material.getMaterialCode());

            // 根据物料id查询是否关联货品,如果关联货品则更新胖总管和巨沃
            CompletableFuture.runAsync(() -> {
                List<Inventory> list = inventoryService.list(new LambdaQueryWrapper<Inventory>()
                        .eq(Inventory::getMaterialId, phMaterialEditDto.getId()));
                if (CollectionUtils.isNotEmpty(list)) {
                    list.stream().forEach(item -> {
                        inventoryApiService.syncThirdParties(item.getId(), phMaterialEditDto.getOperatorName());
                    });
                }
            });
            return true;
        });
    }

    private void editPhMaterialData(PhMaterialEditDto phMaterialEditDto, PhMaterialVo material) {

        LambdaUpdateWrapper<Material> updateSet = Wrappers.lambdaUpdate(Material.class)
                .eq(Material::getId, phMaterialEditDto.getId())
                .set(Material::getMaterialName, phMaterialEditDto.getMaterialName())
                .set(Material::getUpdatedByName, phMaterialEditDto.getOperatorName())
                .set(Material::getUpdatedById, phMaterialEditDto.getOperatorId().intValue())
                .set(Material::getUpdatedTime, LocalDateUtil.dateToLocalDateTime(new Date()))
                .set(Material::getApplyCrowd, phMaterialEditDto.getApplyCrowd())
                .set(Material::getMarketDate, phMaterialEditDto.getMarketDate())
                .set(Material::getMainImage, phMaterialEditDto.getMainImage())
                .set(Material::getOfficialPrice, phMaterialEditDto.getOfficialPrice())
                .set(Material::getSellingPoint, phMaterialEditDto.getSellingPoint());

        // 用户手动输入系列的时候,进行校验,如果不存在则新增
        PhSeries phSeries = phSeriesService.checkSeriesByName(
                phMaterialEditDto.getSeriesName(), material.getBrandId());
        updateSet.set(Material::getSeriesId, phSeries.getId())
                .set(Material::getSeriesName,phSeries.getName());

        // 用户手动输入型号的时候,进行校验,如果不存在,则新增
        PhModel phModel = phModelService.checkPhModelByName(phMaterialEditDto.getModelName(),
                material.getSeriesId(),
                phMaterialEditDto.getBrandId());
        updateSet.set(Material::getModelId, phModel.getId())
                .set(Material::getModelName,phModel.getModel());

        if (!materialService.update(updateSet)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .MATERIAL_EDIT_EXCEPTION_800403);
        }
        // 更新属性
        if (CollectionUtils.isNotEmpty(phMaterialEditDto.getPropertyList())) {
            phMaterialPropertyValueMdService.updatePhmaterialProperty(
                    phMaterialEditDto.getPropertyList(),
                    phMaterialEditDto.getId());
        }
    }

    private Material assembleData(PhMaterialDto phMaterialDto, PhSeries phSeries, PhModel phModel) {
        Material material = new Material();
        BeanUtils.copyProperties(phMaterialDto, material);
        material.setDataType(YesOrNoEnum.YES.getCode());
        material.setMaterialSource(MaterialSourceEnum.PH.getCode());
        material.setMaterialCode(CommonUtils.createAndGetMaterialCode(phMaterialDto.getCategoryId()));
        material.setSeriesId(phSeries.getId());
        material.setModelId(phModel.getId());
        material.setCreatedById(phMaterialDto.getOperatorId().intValue());
        material.setCreatedByName(phMaterialDto.getOperatorName());
        material.setSeriesName(phSeries.getName());
        material.setModelName(phModel.getModel());
        return material;
    }

    public Boolean editPhMaterialWithRecord(
            PhMaterialEditDto phMaterialEditDto,
            PhMaterialVo material,
            BiFunction<PhMaterialEditDto,
                    PhMaterialVo,
                    Boolean> biFunction) {
        CompletableFuture<PhMaterialEditDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {
                    PhMaterialEditDto oldEditDto = ProBeanUtils.copyProperties(phMaterialEditDto, PhMaterialEditDto.class);
                    BeanUtils.copyProperties(material, oldEditDto);
                    if (ObjectUtils.isNotEmpty(material.getApplyCrowd()))
                        oldEditDto.setApplyCrowdString(ApplyCrowdEnum.getName(Integer.valueOf(material.getApplyCrowd())));
                    if (CollectionUtils.isNotEmpty(phMaterialEditDto.getPropertyList())) {
                        String collect = phMaterialPropertyValueMdService.list(
                                        new LambdaQueryWrapper<PhMaterialPropertyValueMd>()
                                                .eq(PhMaterialPropertyValueMd::getMaterialId,
                                                        phMaterialEditDto.getId()))
                                .stream().map(prop -> String.format("[%s：%s]",
                                        prop.getPropertyName(), prop.getPropertyValue()))
                                .collect(Collectors.joining("，"));
                        oldEditDto.setPropertyString(collect);
                    }
                    return oldEditDto;
                });
        Boolean apply = biFunction.apply(phMaterialEditDto, material);

        String property = phMaterialEditDto.getPropertyList()
                .stream()
                .map(prop -> String.format("[%s：%s]",
                        prop.getPropertyName(), prop.getPropertyValue()))
                .collect(Collectors.joining("，"));
        phMaterialEditDto.setPropertyString(property);
        if (ObjectUtils.isNotEmpty(phMaterialEditDto.getApplyCrowd()))
            phMaterialEditDto.setApplyCrowdString(ApplyCrowdEnum.getName(Integer.valueOf(phMaterialEditDto.getApplyCrowd())));
        operationRecordLogsCoreService.operationRecordLogs(oldFuture,
                phMaterialEditDto, "编辑物料信息记录操作日志异常", phMaterialEditDto);

        return apply;
    }

    @Override
    public PhAiPictureVo aiPictureClassify(String imgUrl) {

        PhAiPictureVo phAiPictureVo = new PhAiPictureVo();
        String aiPictureStr = baoxjUtils.aiPictureClassify(imgUrl);
        if (StringUtils.isEmpty(aiPictureStr)) return phAiPictureVo.setHasMatch(false).setErrMsg("包小鉴AI识图服务异常");

        ParserConfig.getGlobalInstance().propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
        PhAiPictureMaterialMd aiPicture = JSON.parseObject(aiPictureStr, PhAiPictureMaterialMd.class);

        if (!aiPicture.getErrorMsg().equals("Success"))
            return phAiPictureVo.setHasMatch(false).setErrMsg("包小鉴：" + aiPicture.getErrorMsg());
        if (!aiPicture.getHasMatch()) return phAiPictureVo.setHasMatch(false).setErrMsg("识别图片失败，请更换图片重试");

        List<PhAiPictureMaterialMd> aiPictures = phAiPictureMaterialMdService.getAiPicturesByItemId(aiPicture.getItemId());
        if (CollectionUtils.isNotEmpty(aiPictures)) {
            List<Long> materialIds = aiPictures.parallelStream().map(PhAiPictureMaterialMd::getMaterialId)
                    .filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
            List<String> materialCodes = aiPictures.parallelStream().map(PhAiPictureMaterialMd::getMaterialCode)
                    .filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
            return ProBeanUtils.copyProperties(aiPicture, phAiPictureVo)
                    .setMaterialIds(CollectionUtils.isEmpty(materialIds) ? null : materialIds)
                    .setMaterialCodes(CollectionUtils.isEmpty(materialCodes) ? null : materialCodes);
        }

        if (!phAiPictureMaterialMdService.save(aiPicture))
            throw new BusinessRuntimeException(INVENTORY_EDIT_EXCEPTION_800216);

        return ProBeanUtils.copyProperties(aiPicture, phAiPictureVo);
    }

    @Override
    public PhAiPictureQueryVo aiPictureQuery(String itemId, Long materialId, String materialCode) {

        PhAiPictureMaterialMd queryMd = phAiPictureMaterialMdService.aiPictureQuery(itemId, materialId, materialCode);

        return ProBeanUtils.copyProperties(queryMd, PhAiPictureQueryVo.class);
    }

    /**
     * 导入物料编码批量包小检AI识图 - 同步
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> aiPictureClassifyBatchSync(List<String> materialCodeList) {

        List<Material> materialList = materialService.getMaterialsByCodes(materialCodeList);
        if (CollectionUtils.isEmpty(materialList))
            throw new BusinessRuntimeException(PARAMETER_IS_INCORRECT_800001);

        // 查询包袋分类下的所有三级分类ID
        Set<Long> level3CategoryIds = phCategoryMapper
                .getCategoryLevel3VosById(1L, null, null)
                .stream().map(PhCategoryLevel3Vo::getLevel3Id).collect(Collectors.toSet());

        CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        List<PhAiPictureMaterialMd> aiPictureList = materialList.stream()
                .filter(material -> {
                    boolean contains = level3CategoryIds.contains(material.getCategoryId());
                    if (!contains) copyOnWriteArrayList.add(material.getMaterialCode());
                    return contains;
                }).map(material -> {
                    String aiPictureStr = baoxjUtils.aiPictureClassify(material.getMainImage());
                    if (StringUtils.isEmpty(aiPictureStr)) {
                        copyOnWriteArrayList.add(material.getMaterialCode());
                        return null;
                    }

                    ParserConfig.getGlobalInstance().propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
                    PhAiPictureMaterialMd aiPicture = JSON.parseObject(aiPictureStr, PhAiPictureMaterialMd.class);
                    if (aiPicture.getErrorMsg().equals("Success") && aiPicture.getHasMatch()) {
                        return aiPicture.setMaterialId(material.getId()).setMaterialCode(material.getMaterialCode());
                    } else {
                        copyOnWriteArrayList.add(material.getMaterialCode());
                        return null;
                    }
                }).filter(ai -> ObjectUtils.isNotEmpty(ai)).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(aiPictureList))
            phAiPictureMaterialMdService.saveOrUpdateBatchByItemIds(aiPictureList);

        return copyOnWriteArrayList;
    }

    /**
     * 导入物料编码批量包小检AI识图 - 异步
     */
    @Override
    public Boolean aiPictureClassifyBatchAsync(List<String> materialCodeList) {
        PhMaterialInfoApiService phMaterialInfoApiService = (PhMaterialInfoApiService) AopContext.currentProxy();
        CompletableFuture.runAsync(() -> {
            ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
            try {
                List<String> failList = phMaterialInfoApiService.aiPictureClassifyBatchSync(materialCodeList);
                String collect = failList.parallelStream().collect(Collectors.joining("\n"));
                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                String fileName = "导入物料编码批量包小检AI识图-识别失败" + time + ".txt";
                bos.write(collect.getBytes(), 0, collect.getBytes().length);
                String failListUrl = QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                log.error("导入物料编码批量包小检AI识图-识别失败 ===>> {}", failListUrl);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("导入物料编码批量包小检AI识图-程序异常 ===>> 入参={}", materialCodeList);
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        return true;
    }

}
